package tencent

import (
	"context"
	"slices"
	"strconv"
	"strings"
	"sync"
	"time"

	"gitee.com/lyuanbo/stock-plus/pkg/logger"
	"gitee.com/lyuanbo/stock-plus/pkg/stock/exchange"
	"gitee.com/lyuanbo/stock-plus/pkg/stock/types"
	"gitee.com/lyuanbo/stock-plus/pkg/stock/xueqiu"
	"github.com/imroc/req/v3"
	"github.com/nats-io/nats.go"
	"github.com/redis/go-redis/v9"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
)

var _ types.Stocker = (*Tencent)(nil)

type Tencent struct {
	ctx    context.Context
	nats   *nats.Conn
	redis  *redis.Client
	logger *logger.Logger

	exchange types.Exchanger

	xq *xueqiu.XueQiu
}

func (t *Tencent) GetExchange(_ ...string) map[string]types.Exchange {
	return nil
}

func New(ctx context.Context, exchange types.Exchanger, xq *xueqiu.XueQiu, nats *nats.Conn, redisClient *redis.Client, logger *logger.Logger) *Tencent {
	t := &Tencent{ctx: ctx, exchange: exchange, xq: xq, nats: nats, redis: redisClient, logger: logger}
	return t
}

func (t *Tencent) Realtime(symbols ...string) ([]*types.Kline, error) {
	isForce := len(symbols) > 0

	if len(symbols) == 0 {
		symbols = t.xq.GetSymbols()
	}

	if len(symbols) == 0 {
		return nil, nil
	}

	// 过滤未开盘的股票
	openSymbols := make([]string, 0)
	for _, symbol := range symbols {
		v := t.xq.GetStockInfo(symbol)
		if (v.Type == "个股" || v.Type == "ETF") && slices.Contains([]string{exchange.SH, exchange.SZ, exchange.HK}, v.Exchange.Code) && t.exchange.IsMarketOpen(v.Exchange.Code) || isForce {
			openSymbols = append(openSymbols, symbol)
		}
	}

	// 每次获取20个股票的实时行情
	var lines []*types.Kline
	var mux sync.Mutex
	var eg errgroup.Group
	for i := 0; i < len(openSymbols); i += 10 {
		end := i + 10
		if end > len(openSymbols) {
			end = len(openSymbols)
		}

		symbols := openSymbols[i:end]

		eg.Go(func() error {
			resp, err := req.C().R().SetQueryParam("q", strings.ToLower(strings.Join(symbols, ","))).Get("https://qt.gtimg.cn")
			if err != nil {
				return err
			}
			// 解析字符串
			// 参考文档: https://www.cnblogs.com/sishahu/p/16638316.html
			// 腾讯API格式: v_sh000001="1~上证指数~000001~3418.33~..."
			// 去除头尾空格+换行符和最后一个分号
			str := strings.Replace(strings.Trim(strings.TrimSpace(resp.String()), ";"), "\n", "", -1)
			arr := strings.Split(str, ";")

			for j := range arr {
				arr1 := strings.Split(arr[j], "=")
				if len(arr1) != 2 {
					continue
				}

				// v_sh513360
				arr2 := strings.Split(arr1[0], "_")
				if len(arr2) != 2 {
					continue
				}

				symbol := strings.ToLower(arr2[1])

				arr3 := strings.Split(arr1[1], "~")
				if len(arr3) == 0 {
					return nil
				}

				var line types.Kline
				// 股票信息
				line.StockInfo = t.xq.GetStockInfo(symbol)
				// 更新交易所信息, 防止缓存的交易所信息过期
				line.StockInfo.Exchange = t.exchange.Get(line.StockInfo.Exchange.Code)

				line.KlineInfo.Close, _ = decimal.NewFromString(arr3[3])
				line.KlineInfo.LastClose, _ = decimal.NewFromString(arr3[4])
				line.KlineInfo.Open, _ = decimal.NewFromString(arr3[5])

				line.KlineInfo.Timestamp, _ = time.ParseInLocation("20060102150405", arr3[30], time.Local)
				line.KlineInfo.Change, _ = decimal.NewFromString(arr3[31])
				line.KlineInfo.ChangePercent, _ = decimal.NewFromString(arr3[32])
				line.KlineInfo.High, _ = decimal.NewFromString(arr3[33])
				line.KlineInfo.Low, _ = decimal.NewFromString(arr3[34])

				// 详细的金额
				row := strings.Split(arr3[35], "/")
				line.KlineInfo.Volume, _ = strconv.ParseInt(row[1], 10, 64)
				line.KlineInfo.Amount, _ = decimal.NewFromString(row[2])
				line.KlineInfo.TurnoverRate, _ = decimal.NewFromString(arr3[38])
				line.KlineInfo.Amplitude, _ = decimal.NewFromString(arr3[43])

				mux.Lock()
				lines = append(lines, &line)
				mux.Unlock()
			}

			return nil
		})
		err := eg.Wait()
		if err != nil {
			t.logger.Warn(t.ctx, "get stock realtime failed", zap.Error(err))
			return nil, err
		}
	}
	return lines, nil
}
